//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.basiclab.iot.core.expression;

import com.basiclab.iot.core.expression.impl.AddExpression;
import com.basiclab.iot.core.expression.impl.BinaryOperationExpression;
import com.basiclab.iot.core.expression.impl.DivideExpression;
import com.basiclab.iot.core.expression.impl.ModExpression;
import com.basiclab.iot.core.expression.impl.MultipleExpression;
import com.basiclab.iot.core.expression.impl.ParamExpression;
import com.basiclab.iot.core.expression.impl.StaticExpression;
import com.basiclab.iot.core.expression.impl.SubtractExpression;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class ExpressionUtil {
    private static final char ADD = '+';
    private static final char SUBTRACT = '-';
    private static final char MULTIPLE = '*';
    private static final char DIVIDE = '/';
    private static final char MOD = '%';
    private static final char LEFT_PARENTHESIS = '(';
    private static final String LEFT_PARENTHESIS_STRING = "(";
    private static final char RIGHT_PARENTHESIS = ')';
    private static final String RIGHT_PARENTHESIS_STRING = ")";
    private static final char VARIABLE = '$';
    private static final String VARIABLE_STRING = "$";
    private static final List<Character> OPERATORS = Arrays.asList('+', '-', '*', '/', '%', '(', ')', '$');
    private static final List<String> VALID_OPERATORS = Arrays.asList(String.valueOf('+'), String.valueOf('-'), String.valueOf('*'), String.valueOf('/'), String.valueOf('%'));

    public ExpressionUtil() {
    }

    public static Expression buildExpression(String expressionRule) throws Exception {
        return buildExpression(parseOperators(expressionRule));
    }

    private static LinkedList<String> parseOperators(String expressionRule) {
        StringBuilder s = null;
        LinkedList<String> operationParts = new LinkedList();
        char[] var3 = expressionRule.trim().toCharArray();
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            char c = var3[var5];
            if (OPERATORS.contains(c)) {
                if (null != s && s.length() > 0) {
                    operationParts.add(s.toString());
                    s = null;
                }

                operationParts.add(String.valueOf(c));
            } else if (Character.isWhitespace(c)) {
                if (null != s && s.length() > 0) {
                    operationParts.add(s.toString());
                    s = null;
                }
            } else if (Character.isDigit(c)) {
                if (null == s) {
                    s = new StringBuilder();
                }

                s.append(c);
            }
        }

        if (null != s && s.length() > 0) {
            operationParts.add(s.toString());
        }

        return operationParts;
    }

    private static Expression buildExpression(LinkedList<String> operators) throws Exception {
        Expression left = null;
        Expression right = null;
        BinaryOperationExpression expression = null;
        LinkedList stack = new LinkedList();

        while(true) {
            while(operators.peek() != null) {
                String operator = (String)operators.poll();
                if (operator.equals("$")) {
                    if (left == null) {
                        left = new ParamExpression();
                    } else {
                        right = new ParamExpression();
                    }
                } else if (VALID_OPERATORS.contains(operator)) {
                    if (expression == null) {
                        expression = getBinaryExpression(operator.charAt(0));
                        expression.setLeft((Expression)left);
                    } else {
                        BinaryOperationExpression subExpression = getBinaryExpression(operator.charAt(0));
                        if (subExpression.getPriority() > expression.getPriority()) {
                            stack.push(expression);
                            left = right;
                            subExpression.setLeft((Expression)right);
                            right = null;
                            expression = subExpression;
                        } else {
                            expression.setRight((Expression)right);

                            while(stack.peek() != null && ((BinaryOperationExpression)stack.peek()).getPriority() >= subExpression.getPriority()) {
                                BinaryOperationExpression previousExpression = (BinaryOperationExpression)stack.poll();
                                previousExpression.setRight(expression);
                                expression = previousExpression;
                            }

                            left = expression;
                            subExpression.setLeft(expression);
                            expression = subExpression;
                            right = null;
                        }
                    }
                } else if (operator.equals("(")) {
                    if (expression != null) {
                        stack.push(expression);
                        left = null;
                        expression = null;
                    }

                    stack.push(new ExpressionUtil.ParenthesesExpression());
                } else if (!operator.equals(")")) {
                    int value = Integer.valueOf(operator);
                    if (left == null) {
                        left = new StaticExpression(value);
                    } else {
                        right = new StaticExpression(value);
                    }
                } else {
                    if (right != null) {
                        expression.setRight((Expression)right);
                        right = null;
                    }

                    while(!(stack.peek() instanceof ExpressionUtil.ParenthesesExpression)) {
                        right = expression;
                        expression = (BinaryOperationExpression)stack.poll();
                        expression.setRight(right);
                    }

                    stack.poll();
                    if (stack.peek() != null) {
                        right = expression;
                        expression = (BinaryOperationExpression)stack.poll();
                        left = expression.getLeft();
                    } else {
                        left = expression;
                        expression = null;
                        right = null;
                    }
                }
            }

            if (right != null) {
                expression.setRight((Expression)right);

                while(stack.peek() != null) {
                    BinaryOperationExpression preExpression = (BinaryOperationExpression)stack.poll();
                    preExpression.setRight(expression);
                    expression = preExpression;
                }
            }

            if (null == expression && null != left) {
                return (Expression)left;
            }

            return expression;
        }
    }

    private static BinaryOperationExpression getBinaryExpression(char operator) throws Exception {
        switch(operator) {
            case '%':
                return new ModExpression();
            case '&':
            case '\'':
            case '(':
            case ')':
            case ',':
            case '.':
            default:
                throw new Exception("Unkown operator " + operator + " for table partition.");
            case '*':
                return new MultipleExpression();
            case '+':
                return new AddExpression();
            case '-':
                return new SubtractExpression();
            case '/':
                return new DivideExpression();
        }
    }

    private static final class ParenthesesExpression extends BinaryOperationExpression {
        private static final long serialVersionUID = -5045991081974652465L;

        private ParenthesesExpression() {
        }

        @Override
        public int getPriority() {
            return 3;
        }

        @Override
        public long getValue(long key) {
            return 0L;
        }
    }
}
